﻿#ifndef _LTMIT_SQL_DB_UTIL_H
#define _LTMIT_SQL_DB_UTIL_H

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string>
#include <vector>
#include <string.h>
#include <memory>
#include "General_exception.h"

namespace sqldb{////////////////
using namespace std;


//================= sqldb =================
#define ERROR_SQLDB_INVALID_URL 21000  //URL 无效
#define ERROR_SQLDB_ARGUMENT_IS_EMPTY 21001 // 参数为空
#define ERROR_SQLDB_INVALID_PARAM 21002 // 参数无效
#define ERROR_SQLDB_ERR_IDX 21003 // 取结果的列id超出范围
#define ERROR_SQLDB_LACK_OF_BIND_PARAM 21004 // 缺少bind parameter
#define ERROR_SQLDB_RECONNECT_ERROR 21005 // 重复连接
#define ERROR_SQLDB_ALLOCATE_MYSQL_STMT_FAILED 21006 // MYSQL_STMT句柄创建失败
#define ERROR_SQLDB_UNSUPPORTED_SQLDB_TYPE 21007 // 不支持的数据库类型
#define ERROR_SQLDB_EXE_ERROR 21008 // 数据库返回错误
#define ERROR_SQLDB_CONNECT_ERROR 21009 // 数据库返回错误

#ifndef SQLDB_ERROR_H_
#define SQLDB_ERROR_H_

#define	SQLDB_ERR_OK	0



#endif /* SQLDB_ERROR_H_ */

//marco switches
#define __SQLDB_ENABLE_MYSQL
//#define __SQLDB_ENABLE_ORACLE
//#define __SQLDB_ENABLE_SQLITE

class DataBase;

///sql-db result set class
class ResultSet{
	ResultSet(const ResultSet&); //no-copy!
	friend class DataBase;
protected:
	ResultSet() {}
public:
	virtual ~ResultSet() throw() {}

	/** cursor move function.
	 * 	When you want to fetch one row from the ResultSet,you should call next() once.
	 * 	Please call this function first before you fetch data using get_xxx functions.
	 * */
	virtual bool next()=0;

	/* For most of the conditions, get_string() give the most compatibility in all situations.
	 *	If you want to read data which the format was not listed below, just try get_string() and convert it.
	 *	get_blob() was intended to get binary data from database (i.e. picture data). In many db-platforms ,
	 *		get_string() may do the same thing, but others not.
	 *	get_datetime() give the 'db-specified-client-format' to show the date and time. Same date-time value in
	 *		different db-platform or in different client environment may get different result.
	 * */

	virtual string get_blob(uint32_t idx) const =0;		//throw!
	virtual string get_string(uint32_t idx) const=0;	//throw!
	virtual int get_int(uint32_t idx) const=0;	//throw!
	virtual int64_t get_int64(uint32_t idx) const=0;	//throw!
	virtual double get_double(uint32_t idx) const=0;	//throw!
	virtual float get_float(uint32_t idx) const=0;	//throw!

	virtual string get_datetime(uint32_t idx) const=0;	//throw!
};

class DbStmt;

enum DbBindType{
	DB_TYPE_NULL=1,
	DB_TYPE_BLOB=2,
	DB_TYPE_STRING=3,
	DB_TYPE_INT=4,
	DB_TYPE_INT64=5,
	DB_TYPE_FLOAT=6,
	DB_TYPE_DOUBLE=7,
	DB_TYPE_INVALID=-1
};

class DbBindPara{
public:
	DbBindType data_type;
	union {
		const void* data_ptr;
		int i_val;
		long long i64_val;
		float f_val;
		double d_val;
	};
	size_t data_size;
	DbBindPara(DbBindType tp,const void* p,size_t s):data_type(tp),data_ptr(p),data_size(s) {}
};

DbBindPara make_bp_null();
DbBindPara make_bp(const void* data,size_t size);
DbBindPara make_bp(const char* str);
DbBindPara make_bp(const string& cpstr);
DbBindPara make_bp(int val);
DbBindPara make_bp(long long val);
DbBindPara make_bp(float val);
DbBindPara make_bp(double val);


///sql-db statement
class Statement {
	Statement(const Statement&);
protected:
	Statement(){}
public:
	virtual ~Statement() throw() {}

	virtual Statement& operator << (const char* str)=0;
	virtual Statement& operator << (const std::string& str)=0;

	virtual Statement& operator << (const DbBindPara& bp)=0;

	virtual void bind_and_exec()=0;
};

///sql-db Database interface
enum DbCtrlCmd{
	cc_ping=1,
	cc_affected_rows=2
};

class DataBase{
	DataBase(const DataBase&) ;//no-copy!
protected:
	DataBase(){}
public:
	virtual ~DataBase() throw() {}
	
	/** create db-instance using specified 'db-type'.
	 * 	Now we support "oracle", "mysql" and "sqlite".
	 * 	Unsupported type will throw an exception!
	 * */
	static shared_ptr<DataBase> CreateInstance(const string& db_type);	//throw!

	/** db connect routine. provide uniformed db-connect function
	 * Note: The format of 'conn-url' may vary.
	 * 		For Mysql: conn_url='host[:port]/initial_db_name'	(i.e. "localhost/mysql", "192.168.1.124:3310/vision_db" )
	 * 		For Oracle: conn_url= service type oracle conn-url or SID (i.e. "//192.168.1.16:1521/orcl.sim166", "ORCL")
	 *		For sqlite: conn_url= sqlite db file path; uname= db open flags
	 *				'c': [create] new db.(if not specified, open existing db)
	 *				'r': open db as [readonly] mode. (if not specified, open for read-write mode(default) )
	 *				'm': open db in [memory]. (if not specified, open db in disk.(default) )
	 * */
	virtual void connect(const string& conn_url,const string& uname,const string& pswd)=0;	//throw!
	
	/** run sql with no result.(for example,'update','delete' operations)
	 *
	 * */
	virtual void exec_no_query(const string& sql)=0;	//throw!

	/** run sql and get resultset. (for example, 'select');
	 *
	 * */
	virtual shared_ptr<ResultSet> exec_query(const string& sql)=0; //throw!

	/* **newly add***
		for extended use!
		do not throw exception
	*/
	virtual int run_ctrl_cmd(int cmd,void* para1=0,void* para2=0,void* para3=0) { return -123456789; }

	virtual shared_ptr<Statement> prepare_stmt()=0; //throw!
};

shared_ptr<DataBase> SqlDb_InitConn(const string& dbtype,const string& conn_url,const string& uname,const string& pswd,const string& init_paras="");


}//////////////////////////////////////

#endif
